/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 overlay_adv_object_image.h

	$Header: /heroes4/overlay_adv_object_image.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( OVERLAY_ADV_OBJECT_IMAGE_H_INCLUDED )
#define OVERLAY_ADV_OBJECT_IMAGE_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

#include <cassert>
#include <deque>
#include <vector>

#include "adventure_tile_size.h"
#include "simple_adv_object_image.h"
#include "uncopyable.h"

class t_adv_object_image_visitor_24;
class t_overlay_adv_object_image_24;
class t_adv_object_image_visitor;
class t_overlay_adv_object_image;

// ------------------------------------------------------------------------------
// t_overlay_adv_object_subimage_base class template
// ------------------------------------------------------------------------------

template < typename t_base, typename t_image >
class t_overlay_adv_object_subimage_base : public t_base
{
public:
	// Constructor
	t_overlay_adv_object_subimage_base(
		t_image const &	image,
		int				column );

	// Member functions
	int						get_column() const;
	virtual int				get_depth_offset() const;
	t_image const &			get_image() const;
	virtual t_screen_rect	get_rect() const;
	virtual t_screen_rect	get_rect( int frame_num ) const;
	virtual bool			is_underlay() const;

private:
	// Data members
	t_image const *	m_image_ptr;
	int				m_column;
};

template < typename t_base, typename t_image >
inline t_overlay_adv_object_subimage_base< t_base, t_image >::t_overlay_adv_object_subimage_base(
	t_image const &	image,
	int				column )
	:	m_image_ptr( &image ),
		m_column( column )
{
}

template < typename t_base, typename t_image >
inline int t_overlay_adv_object_subimage_base< t_base, t_image >::get_column() const
{
	return m_column;
}

template < typename t_base, typename t_image >
int t_overlay_adv_object_subimage_base< t_base, t_image >::get_depth_offset() const
{
	return m_image_ptr->get_column_depth_offset( m_column );
}

template < typename t_base, typename t_image >
inline t_image const & t_overlay_adv_object_subimage_base< t_base, t_image >::get_image() const
{
	return *m_image_ptr;
}

template < typename t_base, typename t_image >
t_screen_rect t_overlay_adv_object_subimage_base< t_base, t_image >::get_rect() const
{
	return m_image_ptr->get_column_rect( m_column );
}

template < typename t_base, typename t_image >
t_screen_rect t_overlay_adv_object_subimage_base< t_base, t_image >::get_rect(
	int	frame_num ) const
{
	return m_image_ptr->get_column_rect( m_column );
}

template < typename t_base, typename t_image >
bool t_overlay_adv_object_subimage_base< t_base, t_image >::is_underlay() const
{
	return false;
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_subimage_24 class
// ------------------------------------------------------------------------------

class t_overlay_adv_object_subimage_24
	:	public	t_overlay_adv_object_subimage_base<
					t_adv_object_subimage_24,
					t_overlay_adv_object_image_24 >
{
public:
	// Constructor
	t_overlay_adv_object_subimage_24(
		t_overlay_adv_object_image_24 const &	image,
		int										column );
};

inline t_overlay_adv_object_subimage_24::t_overlay_adv_object_subimage_24(
	t_overlay_adv_object_image_24 const &	image,
	int										column )
	:	t_overlay_adv_object_subimage_base<
			t_adv_object_subimage_24,
			t_overlay_adv_object_image_24 >( image, column )
{
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_subimage class
// ------------------------------------------------------------------------------

class t_overlay_adv_object_subimage
	:	public	t_overlay_adv_object_subimage_base<
					t_adv_object_subimage,
					t_overlay_adv_object_image >
{
public:
	// Constructor
	t_overlay_adv_object_subimage(
		t_overlay_adv_object_image const &	image,
		int									column );

	// Member functions
	virtual void	draw_to(
						int						frame_num,
						t_screen_rect const &	source_rect,
						t_abstract_bitmap16 &	dest,
						t_screen_point const &	dest_point,
						int						alpha = 15 ) const;
	virtual void	draw_to(
						int						frame_num,
						t_abstract_bitmap16 &	dest,
						t_screen_point const &	dest_point ) const;
};

inline t_overlay_adv_object_subimage::t_overlay_adv_object_subimage(
	t_overlay_adv_object_image const &	image,
	int									column )
	:	t_overlay_adv_object_subimage_base<
			t_adv_object_subimage,
			t_overlay_adv_object_image >( image, column )
{
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image_base_base class
// ------------------------------------------------------------------------------

class t_overlay_adv_object_image_base_base
{
public:
	// Constructors/Destructor
	t_overlay_adv_object_image_base_base();
	explicit t_overlay_adv_object_image_base_base( t_screen_rect const & rect );
	virtual ~t_overlay_adv_object_image_base_base();

	// Member functions
	int				get_column_depth_offset( int col ) const;
	t_screen_rect	get_column_rect( int column_num ) const;
	int				get_left_column() const;
	int				get_right_column() const;
	void			set_column_depth_offset( int col, int new_depth_offset );

protected:
	// Static member functions
	static int	get_left_col( int left_coord );
	static int	get_right_col( int right_coord );

	// Member functions
	void	on_rect_changed(
				t_screen_rect const &	old_rect,
				t_screen_rect const &	new_rect );
	bool	read_version(
				std::streambuf &		stream,
				int						version,
				t_screen_rect const &	old_rect );
	bool	write( std::streambuf & stream ) const;

private:
	// Data members
	std::deque< int >	m_column_depth_offset_deque;

	// Member functions
	virtual t_screen_rect	get_image_rect() const = 0;
};

inline t_overlay_adv_object_image_base_base::t_overlay_adv_object_image_base_base()
{
}

inline t_overlay_adv_object_image_base_base::~t_overlay_adv_object_image_base_base()
{
}

inline int t_overlay_adv_object_image_base_base::get_left_column() const
{
	return get_left_col( get_image_rect().left );
}

inline int t_overlay_adv_object_image_base_base::get_right_column() const
{
	return get_right_col( get_image_rect().right );
}

inline int t_overlay_adv_object_image_base_base::get_left_col( int left_coord )
{
	if ( left_coord >= 0 )
		return left_coord / ( k_adventure_tile_width / 2 );
	return ( left_coord - ( k_adventure_tile_width / 2 - 1 ) ) / ( k_adventure_tile_width / 2 );
}

inline int t_overlay_adv_object_image_base_base::get_right_col( int right_coord )
{
	if ( right_coord >= 0 )
		return ( right_coord + ( k_adventure_tile_width / 2 - 1 ) ) / ( k_adventure_tile_width / 2 );
	return right_coord / ( k_adventure_tile_width / 2 );
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image_base class template
// ------------------------------------------------------------------------------

template < typename t_base, typename t_derived, typename t_subimage_arg >
class t_overlay_adv_object_image_base
	:	public	t_base,
		public	t_overlay_adv_object_image_base_base,
		private	t_uncopyable
{
public:
	// Types
	typedef typename t_base::t_subimage t_subimage;

	// Constructors
	explicit t_overlay_adv_object_image_base( t_derived const & derived );
	template <
		typename	t_other_base,
		typename	t_other_derived,
		typename	t_other_subimage_arg >
	t_overlay_adv_object_image_base(
		t_overlay_adv_object_image_base<
			t_other_base,
			t_other_derived,
			t_other_subimage_arg > const &	other,
		t_derived const &					derived )
		:	t_base( other ),
			t_overlay_adv_object_image_base_base( other ),
			m_derived( derived )
	{
		build_subimage_vector( get_left_column(), get_right_column() );
	}
	t_overlay_adv_object_image_base(
		t_overlay_adv_object_image_base const &	other,
		t_derived const &						derived );
	t_overlay_adv_object_image_base(
		t_base const &		base,
		t_derived const &	derived );

	// Member functions
	virtual t_subimage const &	get_subimage( int subimage_num ) const;
	virtual int					get_subimage_count() const;

protected:
	// Member functions
	virtual void	on_rect_changed(
						t_screen_rect const &	old_rect,
						t_screen_rect const &	new_rect );

private:
	// Data members
	t_derived const &				m_derived;
	std::vector< t_subimage_arg	>	m_subimage_vector;

	// Member functions
	void					build_subimage_vector(
								int	left_col,
								int	right_col );
	virtual t_screen_rect	get_image_rect() const;
};

template < typename t_base, typename t_derived, typename t_subimage_arg >
inline	t_overlay_adv_object_image_base<
			t_base,
			t_derived,
			t_subimage_arg >::t_overlay_adv_object_image_base(
	t_derived const &	derived )
	:	m_derived( derived )
{
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
t_overlay_adv_object_image_base< 
	t_base,
	t_derived,
	t_subimage_arg >::t_overlay_adv_object_image_base(
	t_overlay_adv_object_image_base const &	other,
	t_derived const &						derived )
	:	t_base( other ),
		t_overlay_adv_object_image_base_base( other ),
		m_derived( derived )
{
	m_subimage_vector.reserve( other.m_subimage_vector.size() );
	std::vector< t_subimage_arg >::const_iterator subimage_iter = other.m_subimage_vector.begin();
	for ( ; subimage_iter != other.m_subimage_vector.end(); ++subimage_iter )
		m_subimage_vector.push_back( t_subimage_arg( m_derived, subimage_iter->get_column() ) );
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
t_overlay_adv_object_image_base<
	t_base,
	t_derived,
	t_subimage_arg >::t_overlay_adv_object_image_base(
	t_base const &		base,
	t_derived const &	derived )
	:	t_base( base ),
		t_overlay_adv_object_image_base_base( base.get_rect() ),
		m_derived( derived )
{
	build_subimage_vector( get_left_column(), get_right_column() );
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
typename t_overlay_adv_object_image_base<
	t_base,
	t_derived,
	t_subimage_arg >::t_subimage const &
t_overlay_adv_object_image_base<
	t_base,
	t_derived,
	t_subimage_arg >::get_subimage(
	int	subimage_num ) const
{
	assert( subimage_num >= 0 && subimage_num < m_subimage_vector.size() );
	return m_subimage_vector[ subimage_num ];
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
int	t_overlay_adv_object_image_base<
		t_base,
		t_derived,
		t_subimage_arg >::get_subimage_count() const
{
	return m_subimage_vector.size();
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
void	t_overlay_adv_object_image_base<
			t_base,
			t_derived,
			t_subimage_arg >::on_rect_changed(
	t_screen_rect const &	old_rect,
	t_screen_rect const &	new_rect )
{
	t_overlay_adv_object_image_base_base::on_rect_changed( old_rect, new_rect );

	int new_left_column = get_left_col( new_rect.left );
	int new_right_column = get_right_col( new_rect.right );
	if ( new_left_column != get_left_col( old_rect.left ) || new_right_column != get_right_col( old_rect.right ) )
		build_subimage_vector( new_left_column, new_right_column );
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
void	t_overlay_adv_object_image_base<
			t_base,
			t_derived,
			t_subimage_arg >::build_subimage_vector(
	int	left_col,
	int	right_col )
{
	std::vector< t_subimage_arg > new_subimage_vector;
	new_subimage_vector.reserve( right_col - left_col );
	int col;
	for ( col = left_col; col != right_col; ++col )
		new_subimage_vector.push_back( t_subimage_arg( m_derived, col ) );
	m_subimage_vector.swap( new_subimage_vector );
}

template < typename t_base, typename t_derived, typename t_subimage_arg >
t_screen_rect	t_overlay_adv_object_image_base<
					t_base,
					t_derived,
					t_subimage_arg >::get_image_rect() const
{
	return get_rect();
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image_24 class
// ------------------------------------------------------------------------------

class t_overlay_adv_object_image_24
	:	public	t_overlay_adv_object_image_base<
					t_simple_adv_object_image_24,
					t_overlay_adv_object_image_24,
					t_overlay_adv_object_subimage_24 >
{
public:
	// Constructors
	t_overlay_adv_object_image_24();
	t_overlay_adv_object_image_24(
		t_overlay_adv_object_image_24 const &	other );
	explicit t_overlay_adv_object_image_24(
		t_simple_adv_object_image_24 const &	other );

	// Member functions
	virtual void			accept( t_visitor & visitor );
	virtual void			accept( t_visitor & visitor ) const;
	virtual t_clone_ptr		clone() const;
	virtual t_clone_16_ptr	clone_as_16() const;
	bool					read( std::streambuf & stream );
	bool					read_version(
								std::streambuf &	stream,
								int					version );
	bool					write( std::streambuf & stream ) const;
};

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image class
// ------------------------------------------------------------------------------

class t_overlay_adv_object_image
	:	public	t_overlay_adv_object_image_base<
					t_simple_adv_object_image,
					t_overlay_adv_object_image,
					t_overlay_adv_object_subimage >
{
public:
	// Constructors
	t_overlay_adv_object_image();
	t_overlay_adv_object_image(
		t_overlay_adv_object_image const &	other );
	explicit t_overlay_adv_object_image(
		t_overlay_adv_object_image_24 const &	other );
	explicit t_overlay_adv_object_image(
		t_simple_adv_object_image const &	other );

	// Member functions
	virtual void		accept( t_visitor & visitor );
	virtual void		accept( t_visitor & visitor ) const;
	virtual t_clone_ptr	clone() const;
	void				draw_column_to(
							int						column,
							int						frame_num,
							t_screen_rect const &	source_rect,
							t_abstract_bitmap16 &	dest,
							t_screen_point const &	dest_point,
							int						alpha = 15 ) const;
	void				draw_column_to(
							int						column,
							int						frame_num,
							t_abstract_bitmap16 &	dest,
							t_screen_point const &	dest_point ) const;
};

#endif // !defined( OVERLAY_ADV_OBJECT_IMAGE_H_INCLUDED )
